package common.db;

import java.nio.charset.Charset;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;

import common.db.dao.Dao;
import common.db.dao.DaoSession;
import common.db.jdbc.Jdbc;
import common.db.jdbc.JdbcSession;

import core.config.Factory;
import core.exception.ThrowableUtil;

/**
 * Jdbcソース
 * @author Tadashi Nakayama
 */
public final class JdbcSource {

	/** デフォルト接続先 */
	private static final ThreadLocal<String> DEFAULT_NAME = new ThreadLocal<>();

	/**
	 * デフォルト接続先名設定
	 * @param name デフォルト接続先名
	 */
	public static void setDefaultConnection(final String name) {
		DEFAULT_NAME.set(name);
	}

	/**
	 * デフォルト接続先名削除
	 */
	public static void removeDefaultConnection() {
		DEFAULT_NAME.remove();
	}

	/**
	 * コンストラクタ
	 */
	private JdbcSource() {
		throw new AssertionError();
	}

	/**
	 * DAO取得
	 * @return DAOオブジェクト
	 */
	public static Dao getDao() {
		DaoSession df = Factory.create(DaoSession.class);
		return df.getDao(DEFAULT_NAME.get());
	}

	/**
	 * DAO取得
	 * @param cname 設定名
	 * @return DAOオブジェクト
	 */
	public static Dao getDao(final String cname) {
		DaoSession df = Factory.create(DaoSession.class);
		return df.getDao(cname);
	}

	/**
	 * コネクション取得
	 * @return コネクション
	 */
	public static Jdbc getConnection() {
		JdbcSession cf = Factory.create(JdbcSession.class);
		return Jdbc.wrap(cf.getConnection(DEFAULT_NAME.get()));
	}

	/**
	 * コネクション取得
	 * @param name プロパティ項目名
	 * @return コネクション
	 */
	public static Jdbc getConnection(final String name) {
		JdbcSession cf = Factory.create(JdbcSession.class);
		return Jdbc.wrap(cf.getConnection(name));
	}

	/**
	 * コネクション作成
	 * トランザクション内エラー発生後、またはドライバーコネクションを取得時に使用。
	 * @return コネクション
	 */
	public static Jdbc newConnection() {
		JdbcSession cf = Factory.create(JdbcSession.class);
		return Jdbc.wrap(cf.newConnection(DEFAULT_NAME.get()));
	}

	/**
	 * コネクション作成
	 * トランザクション内エラー発生後、またはドライバーコネクションを取得時に使用。
	 * @param name プロパティ項目名
	 * @return コネクション
	 */
	public static Jdbc newConnection(final String name) {
		JdbcSession cf = Factory.create(JdbcSession.class);
		return Jdbc.wrap(cf.newConnection(name));
	}

	/**
	 * DBエンコーディング取得
	 * @return エンコーディング文字列
	 */
	public static Charset getCharset() {
		JdbcSession cf = Factory.create(JdbcSession.class);
		return cf.getCharset(DEFAULT_NAME.get());
	}

	/**
	 * DBエンコーディング取得
	 * @param name プロパティ項目名
	 * @return エンコーディング文字列
	 */
	public static Charset getCharset(final String name) {
		JdbcSession cf = Factory.create(JdbcSession.class);
		return cf.getCharset(name);
	}

	/**
	 * リソース存在確認
	 * @param name プロパティ項目名
	 * @return 存在する場合 true を返す。
	 */
	public static boolean hasResource(final String name) {
		JdbcSession cf = Factory.create(JdbcSession.class);
		return !Objects.toString(cf.getCharset(name), "").trim().isEmpty();
	}

	/**
	 * ドライバ抹消
	 * @param cl ドライバ読込クラスローダ
	 */
	public static void deregister(final ClassLoader cl) {
		List<Driver> list = new ArrayList<>();
		Enumeration<Driver> drivers = DriverManager.getDrivers();
		while (drivers.hasMoreElements()) {
			list.add(drivers.nextElement());
		}

		for (final Driver driver : list) {
			if (driver.getClass().getClassLoader() == cl) {
				try {
					DriverManager.deregisterDriver(driver);
				} catch (SQLException ex) {
					ThrowableUtil.warn(ex);
				}
			}
		}
	}
}
